/*
 * Description:  通信工具类。
 */
#include "link_base.h"
#include <iostream>
#include "log.h"

namespace COMMON {
int LinkBase::SendMessage(const std::shared_ptr<MsgSock> &msgSock, MessageType type,
    const google::protobuf::MessageLite &message)
{
    LOG_DEBUG("LinkBase::SendMessage");
    const size_t messageSize = message.ByteSizeLong();
    /* 限制发送大小 1-256M(256 * 1024 * 1024)字节 */
    if (messageSize == OK || messageSize > MESSAGE_MAX_SIZE) {
        LOG_ERR("message size exceeds limit, message send failed, size:" FORMAT(lu), messageSize);
        return INVALID_MESSAGE_SIZE;
    }
    if (msgSock == nullptr) {
        LOG_ERR("not connect yet");
        return INVALID_MESSAGE_MSGSOCK;
    }
    /*
    * 信息头是固定的7个字节大小
    * 前四个字节拼成一个uint32_t，表示消息的大小
    * 第五个字节表示消息类型
    * 第六个和第七个字节由前面五个字节作为uint8_t的值相加所得校验和
    * head:  0 1 2 3    4       5 6
    *      [  size  ][ type] [checksum]
    */
    const auto messageByte0 = static_cast<uint8_t>((messageSize >> 24) & 0xff);
    const auto messageByte1 = static_cast<uint8_t>((messageSize >> 16) & 0xff);
    const auto messageByte2 = static_cast<uint8_t>((messageSize >> 8) & 0xff);
    const auto messageByte3 = static_cast<uint8_t>((messageSize >> 0) & 0xff);
    const auto messageByte4 = static_cast<uint8_t>(type);
    const uint16_t checkSum = messageByte0 + messageByte1 +
                                messageByte2 + messageByte3 + messageByte4;
    const auto messageByte5 = static_cast<uint8_t>((checkSum >> 8) & 0xff);
    const auto messageByte6 = static_cast<uint8_t>((checkSum >> 0) & 0xff);

    const uint8_t messageHead[HEAD_SIZE] = {
        messageByte0,
        messageByte1,
        messageByte2,
        messageByte3,
        messageByte4,
        messageByte5,
        messageByte6,
    };
    std::vector<char> buffer(messageSize + HEAD_SIZE);
    std::copy(messageHead, messageHead + HEAD_SIZE, buffer.begin()); // 存放头
    message.SerializeToArray(buffer.data() + HEAD_SIZE, static_cast<int>(messageSize)); // 存放内容
    int ret = msgSock->Send(buffer.data(), static_cast<uint32_t>(buffer.size()));
    if (static_cast<size_t>(ret) != buffer.size()) {
        LOG_ERR("send msg failed, ret:" FORMAT(d) " size:" FORMAT(lu), ret, buffer.size());
        return MSGSOCK_SEND_FAILED;
    }
    return OK;
}

int LinkBase::ReceiveMessage(const std::shared_ptr<MsgSock> &msgSock, MessageType &type,
    std::vector<char> &messageBuffer, const uint32_t timeout)
{
    LOG_DEBUG("LinkBase::ReceiveMessage");
    if (msgSock == nullptr) {
        LOG_ERR("not connect yet");
        return INVALID_MESSAGE_MSGSOCK;
    }

    // 读头信息
    std::vector<char> headBuffer(HEAD_SIZE, OK);
    if (ReadBuffer(msgSock, headBuffer, timeout) != OK) {
        LOG_ERR("receive message head error");
        return READ_BUFFER_FAILED;
    }

    /*
    * 信息头是固定的7个字节大小
    * 前四个字节拼成一个uint32_t，表示消息的大小
    * 第五个字节表示消息类型
    * 第六个和第七个字节由前面五个字节作为uint8_t的值相加所得校验和
    * head:  0 1 2 3    4       5 6
    *      [  size  ][ type] [checksum]
    */
    const auto messageByte0 = static_cast<uint8_t>(headBuffer[0]);
    const auto messageByte1 = static_cast<uint8_t>(headBuffer[1]);
    const auto messageByte2 = static_cast<uint8_t>(headBuffer[2]);
    const auto messageByte3 = static_cast<uint8_t>(headBuffer[3]);
    const auto messageByte4 = static_cast<uint8_t>(headBuffer[4]);
    const auto messageByte5 = static_cast<uint8_t>(headBuffer[5]);
    const auto messageByte6 = static_cast<uint8_t>(headBuffer[6]);

    const uint16_t calculateCheckSum = messageByte0 + messageByte1 +
                                        messageByte2 + messageByte3 + messageByte4;
    const uint16_t receivedCheckSum = (static_cast<uint16_t>(messageByte5) << 8) + messageByte6;
    if (calculateCheckSum != receivedCheckSum) {
        LOG_ERR("receive data error: checksum mismatch");
        return CHECK_SUM_ERROR;
    }
    // 读取数据
    const uint32_t messageSize = (static_cast<uint32_t>(messageByte0) << 24) +
                                 (static_cast<uint32_t>(messageByte1) << 16) +
                                 (static_cast<uint32_t>(messageByte2) << 8) +
                                 (static_cast<uint32_t>(messageByte3) << 0);
    if (messageSize == OK || messageSize > MESSAGE_MAX_SIZE) {
        LOG_ERR("message size error, message recv failed, size:" FORMAT(u), messageSize);
        return INVALID_MESSAGE_SIZE;
    }

    type = static_cast<MessageType>(messageByte4);

    messageBuffer.resize(messageSize, OK);
    return ReadBuffer(msgSock, messageBuffer);
}

void LinkBase::SerializeAndResponse(const google::protobuf::MessageLite &msg, Guest::protobuf::link::Result &res)
{
    LOG_DEBUG("LinkBase::SerializeAndResponse");
    size_t byteSize = msg.ByteSizeLong();
    if (byteSize > MESSAGE_MAX_SIZE) { // 消息长度可能为0
        LOG_ERR("response message size exceeds limit, message serialize failed, size:" FORMAT(lu), byteSize);
        return;
    }
    std::vector<uint8_t> buffer(byteSize, OK);
    msg.SerializeWithCachedSizesToArray(buffer.data());
    res.set_response(buffer.data(), buffer.size());
}

int LinkBase::ReadBuffer(const std::shared_ptr<MsgSock> &msgSock, std::vector<char> &buffer,
    const uint32_t timeout) // 由调用者保证参数合法性
{
    LOG_DEBUG("LinkBase::ReadBuffer");
    std::vector<char> tempBuffer(READ_SIZE);
    uint32_t receivedSize = 0;
    int retry = 60; // 尝试重读60次
    while (receivedSize < buffer.size() && retry) {
        retry--;
        size_t remainSize = buffer.size() - receivedSize;
        uint32_t smallOne = 0;
        if (remainSize < tempBuffer.size()) {
            smallOne = static_cast<uint32_t>(remainSize);
        } else {
            smallOne = static_cast<uint32_t>(tempBuffer.size());
        }
        int ret = msgSock->Recv(tempBuffer.data(), smallOne, timeout);
        if (ret == 0) {
            #ifdef _WIN32
                if (GetLastError() == ERROR_NO_SYSTEM_RESOURCES) {
                    LOG_ERR("read 0 bytes because no resource, retry after 1s");
                    WaitTime(1000); // 1000ms
                    continue;
                }
            #endif
            LOG_ERR("read 0 bytes because " FORMAT(d), PrintError());
            return MSGSOCK_RECV_ERROR;
        }
        if (ret < 0) {
            LOG_ERR("receive data error:" FORMAT(d), PrintError());
            return MSGSOCK_RECV_ERROR;
        }
        std::copy(tempBuffer.data(), tempBuffer.data() + ret, buffer.data() + receivedSize);
        receivedSize += static_cast<uint32_t>(ret);
    }
    return OK;
}

int LinkBase::PrintError()
{
    #ifdef _WIN32
        return GetLastError();
    #else
        return errno;
    #endif
}

void LinkBase::WaitTime(int time)
{
    #ifdef _WIN32
        Sleep(time);
    #else
        usleep(time * 1000); // 将秒转为毫秒,1s = 1000ms
    #endif
}
}
